
#include "RtBase.h"
#include "RtCnLenPkg.h"
#include "RtByteStream.h"

#if !defined (_NEW_PROTO_TP)

CLenPkgConn::CLenPkgConn(CRtConnectionManager::CType inConnType)
: CPkgSender(inConnType)
{
	m_pmbSendLocData = NULL;
	m_pmbRecvLocData = NULL;
	m_pmbLastGet = NULL;
	
	m_dwPDULen = sizeof(DWORD);//Init to be 4 bytes
	m_dwDataLen = 0;
	
	m_disconn_timestamp = CRtTimeValue::GetTimeOfDay();
	
	m_cType = CRtConnectionManager::CTYPE_PDU_LENGTH;
	
	m_pConnConnector = NULL;
}

CLenPkgConn::~CLenPkgConn()
{
	
	m_timer.Cancel();
	Release();
}

void CLenPkgConn::Reset4Recv()
{
	m_dwPDULen = sizeof(DWORD);//Reset to be 4 bytes
	m_dwDataLen = 0;
}

void CLenPkgConn::TryOnDisconnIndicate()
{
	//Only RlbTcpServer may do
	return;
}

void CLenPkgConn::NeedKeepAlive(BOOL bNeedKeepAlive)
{
	//Do nothing
	return;
}

void CLenPkgConn::SetConnConnector(CLenPkgConnConnector* pConnConnector)
{
	
	m_pConnConnector = pConnConnector;
}

void CLenPkgConn::CancelHandShake()
{
	//Do nothing
	return;
}

void CLenPkgConn::Reset()
{
	if(m_pmbSendLocData)
	{
		m_pmbSendLocData->DestroyChained();
		m_pmbSendLocData = NULL;
	}
	if(m_pmbRecvLocData)
	{
		m_pmbRecvLocData->DestroyChained();
		m_pmbRecvLocData = NULL;
	}
	if(m_pmbLastGet)
	{
		m_pmbLastGet->DestroyChained();
		m_pmbLastGet = NULL;
	}
	CRtConnBase::Release();
}

RtResult CLenPkgConn::SendData_i() 
{
	RtResult result = RT_OK;
	
	//DWORD dwLenBeforeSend, dwLenAfterSend = 0;
	
	if(m_pmbSendLocData == NULL)
		return RT_OK;
	
	if(m_pmbSendLocData->GetChainedLength() > 0)//Data can send
	{
		//dwLenBeforeSend = m_pmbSendLocData->GetChainedLength();
		result = m_lowerTransport->SendData(*m_pmbSendLocData);
		if (RT_FAILED(result)) {
			RT_WARNING_TRACE_THIS("CLenPkgConn::SendData_i,"
				" len=" << m_pmbSendLocData->GetChainedLength());
		}
		else {
			RT_ASSERTE(m_pmbSendLocData->GetChainedLength() == 0);
			m_pmbSendLocData->DestroyChained();
			m_pmbSendLocData = NULL;
		}
		//dwLenAfterSend = m_pmbSendLocData->GetChainedLength();
		
		//RT_INFO_TRACE("CLenPkgConn::SendData_i(), Data piece sent " << dwLenBeforeSend - dwLenAfterSend << " Bytes");
	}
	
	return result;
}

RtResult CLenPkgConn::SendData(
		CRtMessageBlock& inData,
		BOOL inSendImmediately,
		BOOL inDestroyData,
		DataPriority inPriority, 
		BOOL inNeedAck)
{
	RtResult result = RT_OK;
	
	if(m_status != STATUS_DATA_CAN_SEND)
	{
		RT_WARNING_TRACE_THIS("CLenPkgConn::SendData RT_ERROR_NOT_AVAILABLE, m_status="<<m_status);
		return RT_ERROR_NOT_AVAILABLE;//Cannot send data now
	}
	
	if(m_pmbSendLocData) {
		RT_ASSERTE(m_pmbSendLocData->GetChainedLength() > 0);
		return RT_ERROR_PARTIAL_DATA;
	}

	DWORD dwDataLen = inData.GetChainedLength();
	RT_ASSERTE_RETURN(dwDataLen > 0, RT_OK);
	
	CRtMessageBlock mb(sizeof(DWORD));
	CRtByteStreamIntel bs(mb);
	bs << dwDataLen;
	mb.Append(&inData);

	result = m_lowerTransport->SendData(mb);
	if (RT_FAILED(result)) {
		RT_WARNING_TRACE_THIS("CLenPkgConn::SendData,"
			" len=" << dwDataLen);
		RT_ASSERTE(!m_pmbSendLocData);
		m_pmbSendLocData = mb.DuplicateChained();
		RT_ASSERTE(m_pmbSendLocData->GetChainedLength() > 0);
	}
	
	if (inDestroyData)
		inData.DestroyChained();

	return RT_OK;
	
/*	if(RT_SUCCEEDED(SendData_i()))
	{
		result = RT_OK;
		
		CRtMessageBlock mb(sizeof(DWORD));
		CRtByteStreamNetwork bs(mb);
		bs << dwDataLen;
		
		if(m_pmbSendLocData == NULL)
		{
			mb.Append(&aData);
			m_pmbSendLocData = mb.DuplicateChained();
			
			if(aPara)
				aPara->m_dwHaveSent = aData.GetChainedLength();
			
			aData.AdvanceChainedReadPtr(aData.GetChainedLength());
		}
		else if(m_pmbSendLocData->GetChainedLength() == 0)
		{
			m_pmbSendLocData->DestroyChained();
			
			mb.Append(&aData);
			m_pmbSendLocData = mb.DuplicateChained();
			
			if(aPara)
				aPara->m_dwHaveSent = aData.GetChainedLength();
			
			aData.AdvanceChainedReadPtr(aData.GetChainedLength());
		}
		
		SendData_i();
	}
	
	RT_INFO_TRACE_THIS("CLenPkgConn::SendData result = " << result << " length = " << len);
	return result;*/
}

RtResult CLenPkgConn::SetOption(
								DWORD aCommand, 
								LPVOID aArg)
{
	CRtMutexGuardT<CRtMutexThreadRecursive>	guard(m_mutex);
	if(m_lowerTransport)
		return m_lowerTransport->SetOption(aCommand, aArg);
	
	return RT_ERROR_NOT_AVAILABLE;
}

RtResult CLenPkgConn::GetOption(
								DWORD aCommand, 
								LPVOID aArg)
{
	RtResult rv = RT_ERROR_NOT_AVAILABLE;
	CRtMutexGuardT<CRtMutexThreadRecursive>	guard(m_mutex);
	
	switch(aCommand)
	{
	case RT_OPT_TRANSPORT_TRAN_TYPE:
		
		if(m_lowerTransport)
		{
			DWORD dwTransType;
			
			rv = m_lowerTransport->GetOption(
				RT_OPT_TRANSPORT_TRAN_TYPE, 
				(LPVOID)&dwTransType);
			
			if(RT_SUCCEEDED(rv))
			{
				*(static_cast<CRtConnectionManager::CType*>(aArg)) 
					= CRtConnectionManager::CTYPE_PDU_LENGTH | dwTransType;
				
				rv = RT_OK;
			}
		}
		
		return rv;
		
	default:
		if(m_lowerTransport)
			return m_lowerTransport->GetOption(aCommand, aArg);
		
		return RT_ERROR_NOT_AVAILABLE;
	}
}

RtResult CLenPkgConn::Disconnect(
								 RtResult aReason)
{
	m_sink = NULL;
	if(m_lowerTransport)
	{
		m_lowerTransport->Disconnect(aReason);
		m_lowerTransport = NULL;
	}
	return RT_OK;

}

void CLenPkgConn::OnReceive(
							CRtMessageBlock &aData,
							IRtTransport *aTrptId,
							CRtTransportParameter *aPara)
{
	RT_ASSERTE(m_lowerTransport == aTrptId);
	
	if(m_pmbLastGet)
	{
		RT_ASSERTE(!m_pmbRecvLocData);
		m_pmbLastGet->Append(aData.DuplicateChained());//Append the new Msg block
		m_pmbRecvLocData = m_pmbLastGet;
		m_pmbLastGet = NULL;
	}
	else
	{
		if(m_pmbRecvLocData)
		{
			m_pmbRecvLocData->DestroyChained();
			m_pmbRecvLocData = NULL;
		}
		
		m_pmbRecvLocData = aData.DuplicateChained();
	}
	
	while(m_pmbRecvLocData->GetChainedLength() > 0)
	{
		if(m_pmbRecvLocData->GetChainedLength() < m_dwPDULen)
		{
			RT_ASSERTE(!m_pmbLastGet);
			m_pmbLastGet = m_pmbRecvLocData;
			m_pmbRecvLocData = NULL;
			
			break;	//Waiting for next data block's coming
		}
		
		if(m_dwDataLen == 0)
		{
			CRtMessageBlock *pTmp = m_pmbRecvLocData->DuplicateChained();
			CRtByteStreamIntel bs(*pTmp);
			bs >> m_dwDataLen;
			pTmp->DestroyChained();
			
			m_dwPDULen += m_dwDataLen;
			
			continue;
		}
		
		if(m_pmbRecvLocData->GetChainedLength() >= m_dwPDULen)
		{
			CRtByteStreamIntel bs(*m_pmbRecvLocData);
			bs >> m_dwDataLen;
			
			if(m_status != STATUS_UNCONNECTED)
			{
				if(m_sink)
				{
					CRtMessageBlock *pTmp = m_pmbRecvLocData->Disjoint(m_dwDataLen);
					m_sink->OnReceive(
						*m_pmbRecvLocData, 
						this, 
						aPara
						);
					
					m_pmbRecvLocData->DestroyChained();
					m_pmbRecvLocData = pTmp;
//					if(m_pmbRecvLocData)
//					{
//						RT_INFO_TRACE_THIS("CLenPkgConn::OnReceive, still have data length="<<m_pmbRecvLocData->GetChainedLength())
//					}
				}
				else
					m_pmbRecvLocData->AdvanceChainedReadPtr(m_dwDataLen);
			}
			else
				m_pmbRecvLocData->AdvanceChainedReadPtr(m_dwDataLen);
			
			Reset4Recv();
		}
	}
	
	//Garbage Reclaim
	if(m_pmbRecvLocData)
	{
		RT_ASSERTE(m_pmbRecvLocData->GetChainedLength() == 0);
		m_pmbRecvLocData = m_pmbRecvLocData->ReclaimGarbage();
	}
	
	if(m_pmbLastGet)
	{
		m_pmbLastGet = m_pmbLastGet->ReclaimGarbage();
	}
}

void CLenPkgConn::OnSend(
						 IRtTransport *aTrptId,
						 CRtTransportParameter *aPara)
{
	RT_INFO_TRACE_THIS("CLenPkgConn::OnSend");
	SetStatus(STATUS_DATA_CAN_SEND);
	SendData_i();
	if(m_sink)
	{
		//RT_INFO_TRACE("CLenPkgConn::OnSend(), TP OnSend()");
		m_sink->OnSend(this, aPara);
	}
}

void CLenPkgConn::OnDisconnect(
							   RtResult aReason,
							   IRtTransport *aTrptId)
{

	RT_INFO_TRACE_THIS("CLenPkgConn::OnDisconnect reason = " << aReason << " transport = " << aTrptId);

	SetStatus(STATUS_UNCONNECTED);
	
	m_lowerTransport->Disconnect((RtResult)RT_ERROR_NETWORK_CONNECT_ERROR);
	{
		CRtMutexGuardT<CRtMutexThreadRecursive>	guard(m_mutex);
		m_lowerTransport = NULL;
	}

	if(m_sink)
	{
		m_sink->OnDisconnect(aReason, this);
		m_sink = NULL;
	}
	//Waiting for a short time to be Removed
	m_disconn_timestamp = 
		CRtTimeValue::GetTimeOfDay() - CRtTimeValue(SERVER_NORMAL_DISCONN_TIMEOUT, 0);
}

void CLenPkgConn::OnTimer(CRtTimerWrapper* aId)
{
	//Timer is no need here
	return;
}

// interface IRtAcceptorConnectorSink
void CLenPkgConn::OnConnectIndication(
									  RtResult aReason,
									  IRtTransport *aTrpt,
									  IRtAcceptorConnectorId *aRequestId)
{
	RT_INFO_TRACE("CLenPkgConn::OnConnectIndication(), m_lowerTransport = " << m_lowerTransport << ", aTrpt = " << aTrpt);
	
	if(m_pConnConnector)//Connector case
	{
		RT_ASSERTE(m_pConnConnector->GetTPConnector() == aRequestId);
		
		if(RT_SUCCEEDED(aReason))
		{
			m_lowerTransport = aTrpt;
			
			m_lowerTransport->OpenWithSink(this);
			
			//Physical connection make, and data can be sent now
			SetStatus(STATUS_DATA_CAN_SEND);
			
			//Callback to Upper layer
			m_pConnConnector->GetConnectorSink()->OnConnectIndication(
				aReason, 
				this, 
				m_pConnConnector);
			
			//Get Transport type
			DWORD dwTransType;
			
			if(m_lowerTransport)
				m_lowerTransport->GetOption(RT_OPT_TRANSPORT_TRAN_TYPE, (LPVOID)&dwTransType);
		}
		else //There are errors, such as timeouted
		{
			//Callback to Upper layer
			m_pConnConnector->GetConnectorSink()->OnConnectIndication(
				aReason, 
				NULL, 
				m_pConnConnector);
		}
		
		return;
	}
	
	if(m_pConnAcceptor.Get())//Acceptor case
	{
		RT_ASSERTE(m_pConnAcceptor->GetTPAcceptor() == aRequestId);
		
		m_lowerTransport = aTrpt;
		
		aTrpt->OpenWithSink(this);
		
		SetStatus(STATUS_DATA_CAN_SEND);
		
		//Callback to Upper layer
		m_pConnAcceptor->GetAcceptorSink()->OnConnectIndication(
			aReason, 
			this, 
			m_pConnAcceptor.ParaIn());
		
		//Get Transport type
		DWORD dwTransType = CRtConnectionManager::CTYPE_NONE;
		
		if(m_lowerTransport)
			m_lowerTransport->GetOption(RT_OPT_TRANSPORT_TRAN_TYPE, (LPVOID)&dwTransType);
		
		return;
	}
}

void CLenPkgConn::SetServerList(CLenPkgConnServerList *pSvrList)
{
	m_pSvrList = pSvrList;
}

void CLenPkgConn::SetConnAcceptor(CLenPkgConnAcceptor* pConnAcceptor)
{
	m_pConnAcceptor = pConnAcceptor;
}

CRtTimeValue CLenPkgConn::GetDisconnTimestamp()
{
	return m_disconn_timestamp;
}

#endif
